#include <assert.h>
#include <stdio.h>
#include <string.h>

#include "locals.h"
#include "../model/globals.h"
#include "../normalization/normalization_globals.h"

NormalizedAtomicConcept*** strict_subsumers;
NormalizedAtomicConcept*** direct_subsumers;
NormalizedAtomicConcept*** equivalents;

int* strict_subsumers_count;
int* direct_subsumers_count;
int* equivalents_count;

void init_hierarchy_computation() {
	int i;
	// direct_subsumers = (NormalizedAtomicConcept***) malloc(normalized_atomic_concept_count * sizeof(NormalizedAtomicConcept**));
	direct_subsumers = (NormalizedAtomicConcept***) malloc(atomic_concept_count * sizeof(NormalizedAtomicConcept**));
	assert(direct_subsumers != NULL);
	// direct_subsumers_count = (int*) malloc(normalized_atomic_concept_count * sizeof(int));
	direct_subsumers_count = (int*) malloc(atomic_concept_count * sizeof(int));
	assert(direct_subsumers_count != NULL);

	// strict_subsumers = (NormalizedAtomicConcept***) malloc(normalized_atomic_concept_count * sizeof(NormalizedAtomicConcept**));
	strict_subsumers = (NormalizedAtomicConcept***) malloc(atomic_concept_count * sizeof(NormalizedAtomicConcept**));
	assert(strict_subsumers != NULL);
	// strict_subsumers_count = (int*) malloc(normalized_atomic_concept_count * sizeof(int));
	strict_subsumers_count = (int*) malloc(atomic_concept_count * sizeof(int));
	assert(strict_subsumers_count != NULL);

	// equivalents = (NormalizedAtomicConcept***) malloc(normalized_atomic_concept_count * sizeof(NormalizedAtomicConcept**));
	equivalents = (NormalizedAtomicConcept***) malloc(atomic_concept_count * sizeof(NormalizedAtomicConcept**));
	assert(equivalents != NULL);
	// equivalents_count = (int*) malloc(normalized_atomic_concept_count * sizeof(int));
	equivalents_count = (int*) malloc(atomic_concept_count * sizeof(int));
	assert(equivalents_count != NULL);

	// for (i = 0; i < normalized_atomic_concept_count; ++i) {
	for (i = 0; i < atomic_concept_count; ++i) {
		strict_subsumers[i] = NULL;
		strict_subsumers_count[i] = 0;
		equivalents[i] = NULL;
		equivalents_count[i] = 0;
	}
	// for (i = 0; i < normalized_atomic_concept_count; ++i) {
	for (i = 0; i < atomic_concept_count; ++i) {
		direct_subsumers[i] = NULL;
		direct_subsumers_count[i] = 0;
	}
}

void add_equivalent(NormalizedAtomicConcept* a, NormalizedAtomicConcept* b) {

	if (a == b) return;

	NormalizedAtomicConcept** tmp;
	tmp = realloc(equivalents[a->id], (equivalents_count[a->id] + 1) * sizeof(NormalizedAtomicConcept*));
	assert(tmp != NULL);
	equivalents[a->id] = tmp;
	equivalents[a->id][equivalents_count[a->id]] = b;
	++equivalents_count[a->id];
}

void add_strict_subsumer(NormalizedAtomicConcept* a, NormalizedAtomicConcept* b) {
	NormalizedAtomicConcept** tmp;

	tmp = realloc(strict_subsumers[a->id], (strict_subsumers_count[a->id] + 1) * sizeof(NormalizedAtomicConcept*));
	assert(tmp != NULL);
	strict_subsumers[a->id] = tmp;
	strict_subsumers[a->id][strict_subsumers_count[a->id]] = b;
	++strict_subsumers_count[a->id];
}

void add_direct_subsumer(NormalizedAtomicConcept* a, NormalizedAtomicConcept* b) {
	NormalizedAtomicConcept** tmp;

	tmp = realloc(direct_subsumers[a->id], (direct_subsumers_count[a->id] + 1) * sizeof(NormalizedAtomicConcept*));
	assert(tmp != NULL);
	direct_subsumers[a->id] = tmp;
	direct_subsumers[a->id][direct_subsumers_count[a->id]] = b;
	++direct_subsumers_count[a->id];
}

void compute_strict_subsumers() {
	int i, judy_return;
	NormalizedConcept* a;
	Word_t index = 0;
	char is_equivalent;

	// for (i = 0; i < normalized_atomic_concept_count; ++i) {
	for (i = 0; i < atomic_concept_count; ++i) {
		a = normalized_atomic_concepts[i];

		// printf("=== %s ===\n",a->description.atomic->name);
		index = 0;
		J1F(judy_return, a->description.atomic->subsumers, index);
		while (judy_return == 1) {
			// careful! check if ((NormalizedConcept*) index)->description.atomic->subsumers is NULL?
			J1T(is_equivalent, ((NormalizedConcept*) index)->description.atomic->subsumers, (Word_t) a);
			if (is_equivalent)
				add_equivalent(a->description.atomic, ((NormalizedConcept*) index)->description.atomic);
			else {
				add_strict_subsumer(a->description.atomic, ((NormalizedConcept*) index)->description.atomic);
				// printf("-->%s\n",((NormalizedConcept*) index)->description.atomic->name);
			}
			J1N(judy_return, a->description.atomic->subsumers, index);
		}
	}
}

void compute_direct_subsumers(NormalizedAtomicConcept* a) {
	Pvoid_t tmp = (Pvoid_t) NULL;
	int i, j, judy_return;
	NormalizedAtomicConcept* strict_subsumer;
	char flag;

	for (i = 0; i < strict_subsumers_count[a->id]; ++i) {
		strict_subsumer = strict_subsumers[a->id][i];
		for (j = 0; j < strict_subsumers_count[strict_subsumer->id]; ++j) {
			J1S(judy_return, tmp, (Word_t) strict_subsumers[strict_subsumer->id][j]);
			if (judy_return == JERR) {
				fprintf(stderr, "could not set strict subsumer, aborting\n");
				exit(-1);
			}
		}
	}

	for (i = 0; i < strict_subsumers_count[a->id]; ++i) {
		J1T(flag, tmp, (Word_t) strict_subsumers[a->id][i]);
		if (!flag)
			add_direct_subsumer(a, strict_subsumers[a->id][i]);
	}
}

/*
void compute_direct_subsumers(NormalizedAtomicConcept* a) {
	Pvoid_t tmp = (Pvoid_t) NULL;
	int i, j, judy_return;
	NormalizedAtomicConcept* strict_subsumer;
	char flag;

	for (i = 0; i < strict_subsumers_count[a->id]; ++i) {
		strict_subsumer = strict_subsumers[a->id][i];
		for (j = 0; j < strict_subsumers_count[strict_subsumer->id]; ++j) {
			J1S(judy_return, tmp, (Word_t) strict_subsumers[strict_subsumer->id][j]);
			if (judy_return == JERR) {
				fprintf(stderr, "could not set strict subsumer, aborting\n");
				exit(-1);
			}
		}
	}

	for (i = 0; i < strict_subsumers_count[a->id]; ++i) {
		J1T(flag, tmp, (Word_t) strict_subsumers[a->id][i]);
		if (!flag)
			add_direct_subsumer(a, strict_subsumers[a->id][i]);
	}
}
*/


void compute_concept_hierarchy() {
	int i;

	init_hierarchy_computation();
	compute_strict_subsumers();
	for (i = 0; i < atomic_concept_count; ++i)
		compute_direct_subsumers(normalized_atomic_concepts[i]->description.atomic);
}
